from .typeDef import ProtoType, proto_type_builder
from .runEnv import Environment

parser = None


def eval_func(node):
    env = Environment.env_set[-1]
    node.eval(env)


def handle_pause():
    # 处理暂停逻辑
    while Environment.pause_flag:
        command = input("Enter command: ")
        command = parser.parse(command)
        eval_func(command)


class Function(ProtoType):
    def __init__(self, data, env: Environment):
        super().__init__('$function$')
        super().set_ele('para', data['para'])
        super().set_ele('body', data['body'])
        self.env = env
        self.prototype = env.prototype if env is not None else None

    def __call__(self, *args, **kwargs):
        para, body = self.get_ele('para'), self.get_ele('body')
        if len(args) != len(para):
            raise Exception(f'Function parameters do not match definition')
        run_env = Environment(self.env)
        if self.prototype is not None:
            for name, val in self.prototype.atr.items():
                run_env.define(name, val)
        for i, para_name in enumerate(para):
            if isinstance(args[i], Node):
                arg_val = args[i].eval(env)
            else:
                arg_val = args[i]
            run_env.define(para_name, arg_val)
        with run_env:
            res = body.eval(run_env)
            self.env.return_reset()
            return res


def calculate(left: ProtoType, right: ProtoType, operator):
    operator_str_map = {
        r'+': 'plus',
        r'-': 'minus',
        r'*': 'times',
        r'/': 'divide',
        r'%': 'mod',
        r'>': 'gt',
        r'<': 'lt',
        r'>=': 'ge',
        r'<=': 'le',
        r'==': 'eq',
        r'!=': 'neq',
        r'or': 'or_operation',
        r'and': 'and_operation',
        r'not': 'not_operation'
    }
    operator_name = operator_str_map.get(operator)
    calculate_fuc = left.get_ele(operator_name)
    if calculate_fuc is None:
        calculate_fuc = right.get_ele(operator_name)
    if calculate_fuc is None:
        raise Exception("Undefined operation type")
    return calculate_fuc(left, right)


class Node:
    def __init__(self):
        pass

    def eval(self, env):
        pass


class Block(Node):
    def __init__(self, sta_dec_list):
        super().__init__()
        self.list = sta_dec_list

    def eval(self, env: Environment, is_prototype=False):
        block_env = env if is_prototype else Environment(env)
        with block_env:
            for sta in self.list:

                if isinstance(sta, ReturnStatement):
                    env.return_call()
                    return sta.eval(block_env)
                else:
                    t = sta.eval(block_env)
                    if block_env.is_return():
                        env.return_call()
                        return t
                if Environment.pause_flag:
                    handle_pause()


class Expression(Node):
    pass


class Literal(Expression):
    def __init__(self, value, literal_type):
        super().__init__()
        self.value = value
        self.type_name = literal_type

    def eval(self, env):
        res = proto_type_builder(self.type_name)
        res.set_ele('@data', self.value)
        return res


class Identifier(Expression):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def eval(self, env: Environment):
        return env.lookup(self.name)


class BinaryExpression(Expression):

    def __init__(self, left, operator, right):
        super().__init__()
        self.left = left
        self.operator = operator
        self.right = right

    def eval(self, env: Environment):
        return calculate(self.left.eval(env), self.right.eval(env), self.operator)


class FunctionCall(Expression):
    def __init__(self, function_obj, arguments):
        super().__init__()
        self.function_obj = function_obj
        self.arguments = arguments

    def eval(self, env: Environment):
        function_entity = self.function_obj.eval(env)
        if len(self.arguments) > 0:
            args = [arg.eval(env) for arg in self.arguments]
        else:
            args = []
        return function_entity(*args)


class ListElement(Expression):
    def __init__(self, obj, pos):
        super().__init__()
        self.obj = obj
        self.pos = pos

    def eval(self, env: Environment):
        pos: ProtoType = self.pos.eval(env)
        if pos.get_ele('@type_name') != '$int$':
            raise Exception("Non integer ordinal")
        pos = pos.get_ele('@data')
        if isinstance(self.obj, ListElement):
            return self.obj.eval(env).get_ele('@data')[pos]
        else:
            assert isinstance(self.obj, str)
            return env.lookup(self.obj).get_ele('@data')[pos]


class ListEntity(Expression):
    def __init__(self, data):
        super().__init__()
        self.data: list = data

    def eval(self, env: Environment):
        data = []
        if len(self.data) > 0:
            for ele in self.data:
                data.append(ele.eval(env))
        res = proto_type_builder('$list$')
        res.set_ele('@data', data)
        return res


class ChainExpression(Expression):
    def __init__(self, expression, identifier):
        super().__init__()
        self.expression = expression
        self.identifier = identifier

    def eval(self, env):
        entity = self.expression.eval(env)
        return entity.get_ele(self.identifier)


class Declaration(Node):
    pass


class VariableDeclaration(Declaration):
    def __init__(self, name, expression=None):
        super().__init__()
        self.name = name
        self.expression = expression

    def eval(self, env: Environment) -> None:
        if self.expression is None:
            value = proto_type_builder()
        else:
            value = self.expression.eval(env)
        env.define(self.name, value)


class FunctionDeclaration(Declaration):
    def __init__(self, name, parameters, body):
        super().__init__()
        self.name = name
        self.parameters = parameters
        self.body = body

    def eval(self, env: Environment):
        function = Function({'para': self.parameters, 'body': self.body}, env)
        env.define(self.name, function)


class PrototypeDeclaration(Declaration):
    def __init__(self, name, body):
        super().__init__()
        self.name = name
        self.body = body
        self.env = None

    def eval(self, env: Environment):
        if self.env is None:
            self.env = env
        res = proto_type_builder(self.name)
        proto_type_env = Environment(env)
        proto_type_env.prototype = res
        self.body.eval(proto_type_env, True)

        for key, item in proto_type_env.bindings.items():
            res.set_ele(key, item)
        res.register()
        env.define(self.name, self)
        return res

    def __call__(self):
        return self.eval(self.env)


class Statement(Node):
    pass


class ReturnStatement(Statement):
    def __init__(self, expression):
        super().__init__()
        self.expression = expression

    def eval(self, env):
        env.return_call()
        return self.expression.eval(env)


class ExpressionStatement(Statement):
    def __init__(self, expression):
        super().__init__()
        self.expression = expression

    def eval(self, env):
        return self.expression.eval(env)


class IfStatement(Statement):
    def __init__(self, condition, then_branch, else_branch=None):
        super().__init__()
        self.condition = condition
        self.then_branch = then_branch
        self.else_branch = else_branch

    def eval(self, env):
        condition: ProtoType = self.condition.eval(env)
        if condition.get_ele('@data'):
            self.then_branch.eval(env)
        elif self.else_branch is not None:
            self.else_branch.eval(env)


class LoopStatement(Statement):
    def __init__(self, condition, body):
        super().__init__()
        self.condition = condition
        self.body = body

    def eval(self, env):
        condition: ProtoType = self.condition.eval(env)
        if condition.get_ele('@type_name') not in ('$bool$', '$int$'):
            raise Exception("Illegal conditional expression")
        while condition.data:
            self.body.eval(env)


class TryCatchStatement(Statement):
    def __init__(self, try_block, catch_block):
        super().__init__()
        self.try_block = try_block
        self.catch_block = catch_block

    def eval(self, env):
        pass
        # TODO


class AssignStatement(Statement):
    def __init__(self, t_expression, expression):
        super().__init__()
        self.t_expression = t_expression
        self.expression = expression

    def eval(self, env: Environment):
        entity: ProtoType = self.t_expression.eval(env)
        entity.copy(self.expression.eval(env))


class Program(Node):
    def __init__(self, statements):
        super().__init__()
        self.statements = statements

    def eval(self, env):
        return self.statements.eval(env)


class TypedBinaryExpression(BinaryExpression):
    def __init__(self, left, operator, type_name, right):
        super().__init__(left, operator, right)
        self.type_name = type_name

    def eval(self, env: Environment):
        def type_change(var: ProtoType):
            func = var.get_ele('to_' + self.type_name)
            try:
                res = func(var)
                return res
            except e:
                raise Exception('Undefined type conversion')

        left = self.left.eval(env)
        right = self.right.eval(env)
        left = type_change(left)
        right = type_change(right)
        return calculate(left, right, self.operator)
